package org.carota.cret;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;


/**
 * @author : AnWen
 * @version :1.0
 * @email : anwen375@qq.com
 * @since : 2023-03-22 15:18
 */

public class X509CertDaoImpl implements X509Dao {
    //存储证书keyStore类型
    public static final String DEFAULT_KEY_TYPE = "PKCS12";

    //系统添加BC加密算法，以后系统中调用的算法都是BC的算法
    static {
        Security.addProvider (new BouncyCastleProvider ());
    }

    @Override
    public void createCert(String issuer, String subject, Date notBefore, Date notAfter, String certDestPath, BigInteger serial, String keyPassword, String alias) throws Exception {
        //产生公私钥对
        KeyPairGenerator kpg = KeyPairGenerator.getInstance ("RSA");
        kpg.initialize (2048);
        KeyPair keyPair = kpg.generateKeyPair ();
        PublicKey publicKey = keyPair.getPublic ();
        PrivateKey privateKey = keyPair.getPrivate ();

        //组装证书
        X500Name issueDn = new X500Name (issuer);
        X500Name subjectDn = new X500Name (subject);

        //组装公钥信息
        SubjectPublicKeyInfo subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance (new ASN1InputStream (publicKey.getEncoded ()).readObject ());

        X509v3CertificateBuilder builder = new X509v3CertificateBuilder (issueDn, serial, notBefore, notAfter, subjectDn, subjectPublicKeyInfo);

        //证书签名数据
        ContentSigner signGen = new JcaContentSignerBuilder ("SHA1withRSA").build (privateKey);
        X509CertificateHolder holder = builder.build (signGen);
        byte[] certBuf = holder.getEncoded ();
        X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance ("X509")
                .generateCertificate (new ByteArrayInputStream (certBuf));

        //创建KeyStore,存储证书
        KeyStore store = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        store.load (null, null);
        store.setKeyEntry (alias, keyPair.getPrivate (), keyPassword.toCharArray (), new Certificate[]{certificate});
        FileOutputStream out = new FileOutputStream (certDestPath);
        store.store (out, keyPassword.toCharArray ());
        out.close ();
    }

    @Override
    public void printCert(String certPath, String keyPassword) throws Exception {
        char[] charArray = keyPassword.toCharArray ();
        KeyStore ks = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        FileInputStream fis = new FileInputStream (certPath);
        ks.load (fis, charArray);
        fis.close ();
        System.out.println ("keystore type=" + ks.getType ());
        Enumeration<String> enumas = ks.aliases ();
        while (enumas.hasMoreElements ()) {
            String keyAlias =  enumas.nextElement ();
            System.out.println ("alias=[" + keyAlias + "]");
            System.out.println ("is key entry=" + ks.isKeyEntry (keyAlias));
            PrivateKey prikey = (PrivateKey) ks.getKey (keyAlias, charArray);
            Certificate cert = ks.getCertificate (keyAlias);
            PublicKey pubkey = cert.getPublicKey ();
            System.out.println ("cert class = " + cert.getClass ().getName ());
            System.out.println ("cert= " + cert);
            System.out.println ("public key = " + pubkey);
            System.out.println ("private key = " + prikey);
        }
    }

    @Override
    public PublicKey getPublicKey(String certPath, String keyPassword) throws Exception {
        char[] charArray = keyPassword.toCharArray ();
        KeyStore ks = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        FileInputStream fis = new FileInputStream (certPath);
        ks.load (fis, charArray);
        fis.close ();
        Enumeration<String> enumas = ks.aliases ();
        if (enumas.hasMoreElements ()) {
            String keyAlias =  enumas.nextElement ();
            return ks.getCertificate (keyAlias).getPublicKey ();
        }
        return null;
    }

    @Override
    public PrivateKey getPrivateKey(String certPath, String keyPassword) throws Exception {
        char[] charArray = keyPassword.toCharArray ();
        KeyStore ks = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        FileInputStream fis = new FileInputStream (certPath);
        ks.load (fis, charArray);
        fis.close ();
        Enumeration<String> enumas = ks.aliases ();
        if (enumas.hasMoreElements ()) {
            String keyAlias =  enumas.nextElement ();
            return (PrivateKey) ks.getKey (keyAlias, charArray);
        }
        return null;
    }

    @Override
    public void certDelayTo(Date endTime, String certPath, String keyPassword) throws Exception {

    }

    @Override
    public void changePassword(String certPath, String oldPwd, String newPwd) throws Exception {
        KeyStore ks = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        FileInputStream fis = new FileInputStream (certPath);
        ks.load (fis, oldPwd.toCharArray ());
        fis.close ();
        FileOutputStream fout = new FileOutputStream (certPath);
        ks.store (fout, newPwd.toCharArray ());
        fout.close ();
    }

    @Override
    public void deleteAlias(String certPath, String keyPassword, String alias, String entry) throws Exception {
        char[] charArray = keyPassword.toCharArray ();
        KeyStore ks = KeyStore.getInstance (DEFAULT_KEY_TYPE);
        FileInputStream fis = new FileInputStream (certPath);
        ks.load (fis, charArray);
        fis.close ();
        if (ks.containsAlias (alias)) {
            ks.deleteEntry (entry);
            FileOutputStream fout = new FileOutputStream (certPath);
            ks.store (fout, keyPassword.toCharArray ());
            fout.close ();
        } else {
            throw new Exception ("该证书未包含别名------->" + alias);
        }
    }


    public static void main(String[] args) throws Exception {

        //创建证书
        X509CertDaoImpl impl = new X509CertDaoImpl ();
        //颁发者
        String issuer = "C=CN,ST=AnHui,L=HeFei,OU=AHCA,CN=AHCASM2";
        //主体
        String subject = "C=CN,ST=BJ,L=Beijing,O=测试颁布者,OU=F54SX4215,CN=测试签章科技有限公司";

        String certDestPath = "C:\\Users\\anwen\\Desktop\\pdf\\test1.p12";
        BigInteger serial = BigInteger.valueOf (System.currentTimeMillis ());
        String keyPassword = "123456";
        String alias = "数字签名，不可否认，加密密钥，密钥协议，客户端认证，电子邮件保护";

        Calendar calendar = Calendar.getInstance ();
        calendar.add (Calendar.YEAR, 5);

        impl.createCert (issuer,subject, new Date (), calendar.getTime (), certDestPath, serial, keyPassword, alias);

        System.out.println (impl.getPublicKey (certDestPath, "123456"));


    }

}
